package com.bilibili.chatonline.util.db;

import com.bilibili.chatonline.common.CommonText;
import com.bilibili.chatonline.util.ReadProperties;
import lombok.Data;
import lombok.SneakyThrows;


import javax.sql.DataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.logging.Logger;

@Data
public class DBPool implements DataSource{
    private static volatile DBPool instance =null;


    private final Queue<ConcreteConnection> pool;
    //初始连接数
    private final int initSize;
    // 最大连接数
    private final int maxSize;
    // 计数器
    private int counter=0;
    // 定时缩容管理器
    private final ConnectionPoolShrinker shrinker;



    /**
     * 构造函数
     */
    private DBPool() throws IOException {
        initSize=Integer.parseInt(ReadProperties.getProperty(CommonText.DataSource+"pool.init-size"));
        maxSize=Integer.parseInt(ReadProperties.getProperty(CommonText.DataSource+"pool.max-size"));
        this.pool = new ArrayDeque<>(maxSize);
        // 初始化连接池
        initPool();
        // 启动定时缩容任务
        this.shrinker = new ConnectionPoolShrinker(this);
        this.shrinker.start();

    }

    //在一开始就加载数据库驱动
    static {
        try {
            Class.forName(ReadProperties.getProperty(CommonText.DataSource+"driver-class-name"));
        } catch (ClassNotFoundException | IOException e) {
            throw new RuntimeException("没有找到数据库连接池驱动");
        }
    }




/**
 * 获取单例连接池对象
 * */
    public  static DBPool getInstance() throws IOException {
        if(instance==null){
            synchronized (DBPool.class) {
                if (instance == null) {
                    instance = new DBPool();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化连接池
     */
    private void initPool() {
        synchronized (this) {
            for (int i = 0; i < initSize; i++) {
                Connection connection = createConnection();
                if (connection != null) {
                    pool.offer(new ConcreteConnection(connection));
                }
            }
            System.out.println("连接池初始化完成，初始连接数: " + pool.size() +
                    "，总连接数: " + counter);
        }
    }

    /**
     * 创建新的数据库连接
     */
    private Connection createConnection() {
        try {
            Connection connection = DriverManager.getConnection(
                    ReadProperties.getProperty(CommonText.DataSource+"url"),
                    ReadProperties.getProperty(CommonText.DataSource+"username"),
                    ReadProperties.getProperty(CommonText.DataSource+"password")
            );
            System.out.println("创建新连接: " + connection);
            synchronized (this) {
                counter++;
            }
            return connection;
        } catch (SQLException | IOException e) {
            System.err.println("创建数据库连接失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 获取连接（无可用连接时等待）
     */
    @SneakyThrows
    public Connection getConnection() {
        synchronized (this) {
            // 如果连接池为空且还能创建新连接，则创建新连接
            if (pool.isEmpty() && counter < maxSize) {
                Connection conn = createConnection();
                if (conn != null) {
                    System.out.println("创建连接成功: " + conn + "，剩余可用: " + pool.size()+ "，counter: " +counter);
                    return conn;
                }
            }

            // 如果连接池为空且已达最大连接数，则等待
            while (pool.isEmpty()) {
                System.out.println("无可用连接，线程等待: " + Thread.currentThread().getName());
                this.wait(); // 释放锁并等待
            }

            ConcreteConnection conn = pool.poll();


            // 检查连接有效性
            if (isValid(conn.getConnection())) {
                System.out.println("获取连接成功: " + conn + "，剩余可用: " + pool.size());
                return conn.getConnection();
            } else {
                System.out.println("检测到无效连接，正在重建...");
                closeConnection(conn.getConnection());
                counter--;
                return createConnection();
            }
        }

    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }




    /**
     * 归还连接
     */
    public void returnConnection(Connection conn) throws SQLException {
        if (conn == null ) return;

        synchronized (this) {
            if(pool.size()>=maxSize){
                conn.close();
                counter--;
            }else{
                System.out.println(counter);
                pool.offer(new ConcreteConnection(conn));
                System.out.println("连接归还成功: " + conn + "，当前可用: " + pool.size());
                this.notifyAll(); // 唤醒所有等待获取连接的线程
            }

        }
    }

    /**
     * 检查连接是否有效
     */
    private boolean isValid(Connection conn) {
        try {
            return conn != null && !conn.isClosed() && conn.isValid(3);
        } catch (SQLException e) {
            return false;
        }
    }

    /**
     * 关闭真实连接
     */
    protected void closeConnection(Connection conn) {
        try {
            if (conn != null && !conn.isClosed()) {
                conn.close();
                System.out.println("真实连接已关闭: " + conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接池大小
     * */
    public int getPoolSize() {
        synchronized ( this){
            return pool.size();
        }
    }


    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
